// File: jmcomic-core/src/main/java/dev/jukomu/common/base/packer/AbstractPacker.java
package dev.jukomu.common.base.packer;

import dev.jukomu.common.util.file.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.function.BiFunction;

/**
 * Packer接口的抽象实现，提供文件操作模板。
 */
public abstract class AbstractPacker implements Packer {

    protected String packMode = "w"; // Python mode, 'w' for write text
    protected String unpackMode = "r"; // Python mode, 'r' for read text
    protected Charset encoding = StandardCharsets.UTF_8;

    @Override
    public void pack(Object obj, String filepath) throws IOException {
        // 修正: withFile 签名需要匹配 dump 方法可能抛出的异常，这里 dump 只需要 IOException
        // dump 方法的 BiFunction 中，lambda 表达式处理 IOException
        // withFile 方法的 lambda 表达式如果抛出受检异常，需要将其包装为 RuntimeException 或在 withFile 签名中声明
        // 为了简化，我们让 dump 和 load 方法自行处理 IOException，withFile 只负责文件打开/关闭
        // 或者让 withFile 的 visitor 接受一个可能抛出 IOException 的函数
        // 最佳实践是让withFile的visitor函数式接口也声明抛出IOException
        // 这里我们将通过修改withFile的lambda来处理受检异常
        try {
            withFile(filepath, packMode, (file, data) -> {
                try {
                    dump(file, data);
                    return null; // dump方法不返回值，这里返回null
                } catch (IOException e) {
                    throw new RuntimeException(e); // 包装为非受检异常
                }
            }, obj);
        } catch (RuntimeException e) {
            if (e.getCause() instanceof IOException) {
                throw (IOException) e.getCause(); // 重新抛出原始IOException
            }
            throw e;
        }
    }

    @Override
    public <T> T unpack(String filepath, Class<T> clazz) throws IOException, ClassNotFoundException {
        // 修正: withFile 签名需要匹配 load 方法可能抛出的异常
        try {
            return withFile(filepath, unpackMode, (file, cls) -> {
                try {
                    return load(file, cls);
                } catch (IOException e) {
                    throw new RuntimeException(e); // 包装为非受检异常
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e); // 包装为非受检异常
                }
            }, clazz);
        } catch (RuntimeException e) {
            if (e.getCause() instanceof IOException) {
                throw (IOException) e.getCause();
            } else if (e.getCause() instanceof ClassNotFoundException) {
                throw (ClassNotFoundException) e.getCause();
            }
            throw e;
        }
    }

    /**
     * 将对象序列化到文件。
     *
     * @param file 文件对象
     * @param obj  要序列化的对象
     * @throws IOException 如果序列化失败
     */
    protected abstract void dump(File file, Object obj) throws IOException;

    /**
     * 从文件反序列化对象。
     *
     * @param file  文件对象
     * @param clazz 目标类型
     * @param <T>   目标类型
     * @return 反序列化后的对象
     * @throws IOException            如果反序列化失败
     * @throws ClassNotFoundException 如果找不到目标类
     */
    protected abstract <T> T load(File file, Class<T> clazz) throws IOException, ClassNotFoundException;

    /**
     * 使用文件进行操作的模板方法。
     * 注意：这里将 visitor 的签名修改为可以抛出受检异常的 Supplier/Function，
     * 并在 withFile 方法内部捕获并重新抛出。
     *
     * @param filepath  文件路径
     * @param mode      文件打开模式（对应Python的'r', 'w', 'rb', 'wb'）
     * @param visitor   文件操作函数，接收File对象和额外数据，并可能抛出Exception
     * @param extraData 传递给visitor的额外数据（例如要序列化的对象或目标Class）
     * @param <T>       返回类型
     * @param <U>       额外数据类型
     * @return visitor的返回值
     * @throws IOException 如果文件操作失败
     */
    // 修正 withFile 的签名以适应 lambda 表达式抛出的异常，或者让 lambda 包装异常
    // 更通用且符合Java习惯的做法是让withFile的visitor直接抛出Exception，然后在withFile方法声明中捕获或传递
    // 或者，如当前修改所示，让lambda内部包装RuntimeException，外部解包。
    protected <T, U> T withFile(String filepath, String mode, BiFunction<File, U, T> visitor, U extraData) throws IOException {
        File file = new File(filepath);
        FileUtils.getDirPath(filepath, true); // Ensure parent directory exists

        // Python's open mode is abstracted here. For Java, we directly use Files methods.
        // The mode parameter indicates whether it's for reading or writing.
        if (mode.contains("w")) { // Write mode
            // Ensure file is created or truncated
            if (file.exists() && !file.isFile()) {
                throw new IOException("Cannot write to a non-file path: " + filepath);
            }
        } else if (mode.contains("r")) { // Read mode
            if (!file.exists()) {
                // 如果文件不存在，这里直接抛出IOException，与Python的open("r")行为一致
                throw new IOException("File not found for reading: " + filepath);
            }
        }

        // visitor 的 apply 方法可以抛出 RuntimeException，外部调用者捕获并解包
        return visitor.apply(file, extraData);
    }


    /**
     * 辅助方法，用于将Java对象转换为Jackson/Gson可序列化的Map。
     * 如果对象本身是List/Map/基本类型，直接返回。
     * 如果是POJO，Jackson/Gson会自动处理。
     *
     * @param obj 要转换的对象
     * @return 转换后的Map或原始对象
     */
    protected Object toSerializableForm(Object obj) {
        // Jackson/Gson can directly serialize most POJOs, Lists, Maps, and primitives.
        // This method is mostly a placeholder for Python's __dict__ introspection.
        return obj;
    }
}